﻿
using System;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using MapSurface;
using OSMLib;
using RoadPlot;
using Route;

namespace LaoMap
{
    delegate void delegateReport(string sz);

    class OsmRender
    {
        public DelegateToggleWay _delegateToggleWay;
        public delegateReport _delegateReport;

        readonly ISurfaceLinear _surface;

        // Dictionary<int, Trek> _selection;

        Canvas _pickerCanvas;

        public OsmRender(Surface surface)
        {
            _surface = surface;
        }

        public void DoOsmPlotPicker(Canvas canvas, Dictionary<int, Trek> selection)
        {
            _pickerCanvas = canvas;
            DoOsmRoadPicker(canvas);
        }

        public void DoOsmPlotPickerMap(Canvas canvas)
        {
            List<RoadRoute> routes = new List<RoadRoute>();

            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsRoad)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = w.RoadWidth;

                        if (((size >= 6.0) || _surface.XScale >= 50000) && (size > 1))
                        {
                            if (_surface.XScale <= 100000)
                            {
                                size *= 2.0 / 3.0;
                            }

                            List<Point> points = new List<Point>();

                            foreach (var nn in w.Nodes)
                            {
                                OsmNode n1 = ((OsmNode) nn);

                                double px = _surface.GetPx(n1._wgs.Longitude);
                                double py = _surface.GetPy(n1._wgs.Latitude);
                                points.Add(new Point(px, py));
                            }
                            double roadWidth = size * 6;
                            RoadRoute rr = new RoadRoute(points, RoadLegend.RoadLengendFactory(w.GetName, roadWidth* 4), size * 4, size * 3, roadWidth);
                            routes.Add(rr);
                        }
                    }
                }
            }

            Road.DrawBorderedRoutes(routes, canvas, false);
        }

        public void DoOsmPlotBorder(Canvas canvas)
        {
            DoOsmPlotLaosBoundary(canvas);
        }

        public void DoOsmPlot(Canvas canvas)
        {
            DoOsmPlotNationalPark(canvas);
            DoOsmPlotNaturalWood(canvas);
            DoOsmPlotWaterway(canvas); //rivers

            DoOsmPlotNaturalWater(canvas);
            DoOsmPlotNaturalLand(canvas);
            DoOsmPlotCommunityNames(canvas);

            DoOsmPlotBoundary(canvas);
            DoOsmPlotResidentialArea(canvas);

            DoOsmPlotLeisureGarden(canvas);
            DoOsmPlotBuilding(canvas);
            DoOsmPlotAeroway(canvas);
            DoOsmPlotFerry(canvas);
            DoOsmPlotRailway(canvas);
            DoOsmPlotRoad(true, canvas);
            DoOsmPlotRoad(false, canvas);
            DoOsmPlotBuildingNames(canvas);
            DoOsmPlotOther(canvas);
        }

        void ToggleOnMouseButtonDown(object o, MouseEventArgs args)
        {
            Polyline p = o as Polyline;
            if (p == null)
                return;
            OsmWay w = p.Tag as OsmWay;
            if (w == null)
                return;

            if (_delegateToggleWay != null)
            {
                _delegateToggleWay(w._id);
            }

            RedrawPicker();
        }

        public void RedrawPicker()
        {
            _pickerCanvas.Children.Clear();
            DoOsmRoadPicker(_pickerCanvas);
        }




        void DoOsmPlotCommunityNames(Canvas canvas)
        {
            foreach (OsmNode w in Osm.NamedNodes)
            {
                if (_surface.NodeVisible(w))
                {
                    PlaceCommunityBlobNode(w, canvas);
                }
            }
        }

        void DoOsmPlotBuildingNames(Canvas canvas)
        {
            foreach (OsmNode w in Osm.NamedNodes)
            {
                if (_surface.NodeVisible(w))
                {
                    PlaceBuildingBlobNode(w, canvas);
                }
            }
        }

        void DoOsmPlotOther(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && !w.IsTagless && !w.IsRoad && !w.IsWaterway && !w.IsBoundary &&
                        !w.IsAerialImagery && !w.IsNaturalWater && !w.IsLanduseResidential && !w.IsBuilding &&
                        !w.IsBarrier && !w.IsNationalPark && !w.IsAeroway && !w.IsRailway && !w.IsNaturalWood && !w.IsLeisureGarden &&
                        !w.IsNaturalLand && !w.IsFerry
                    )
                {
                    PlaceBlob(w, canvas);
                }
            }
        }

        void DoOsmPlotLaosBoundary(Canvas canvas)
        {
            int nationalSize = 2;

            if (_surface.XScale >= 1000)
                nationalSize = 4;

            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsBoundary && w.IsNationalBoundary && w.IsLaosBoundary)
                {
                    AddPolyline(w, Brushes.Purple, nationalSize, canvas, PolylineOnEnter, null);
                }
            }
        }

        void DoOsmPlotBoundary(Canvas canvas)
        {
            int nationalSize = 2;

            if (_surface.XScale >= 1000)
                nationalSize = 4;

            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsBoundary)
                {
                    if (w.IsNationalBoundary)
                    {
                        if (w.IsLaosBoundary)
                        {
                            AddPolyline(w, Brushes.Purple, nationalSize, canvas, PolylineOnEnter, null);
                        }
                    }
                    else
                    {
                        AddPolyline(w, Brushes.Purple, 1, canvas, PolylineOnEnter, null);
                    }
                }
            }
        }


        void DoOsmPlotNaturalWater(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsNaturalWater)
                {
                    AddPolygon(w, Brushes.LightBlue, canvas);
                }
            }
        }

        private void DoOsmPlotNaturalWood(Canvas canvas)
        {
            if (_surface.XScale < 250)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsNaturalWood)
                {
                    AddPolygon(w, Brushes.DarkGreen, canvas);
                }
            }
        }

        private void DoOsmPlotNationalPark(Canvas canvas)
        {
            if (_surface.XScale < 100)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsNationalPark)
                {
                    AddPolygon(w, Brushes.LightGreen, canvas);
                }
            }
        }

        private void DoOsmPlotResidentialArea(Canvas canvas)
        {
            if (_surface.XScale < 10000)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsLanduseResidential)
                {
                    AddPolygon(w, Brushes.LightGray, canvas);
                }
            }
        }


        private void DoOsmPlotNaturalLand(Canvas canvas)
        {
            if (_surface.XScale < 1000)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsNaturalLand)
                {
                    AddPolygon(w, Brushes.LightYellow, canvas);
                }
            }
        }

        private void DoOsmPlotBuilding(Canvas canvas)
        {
            if (_surface.XScale < 10000)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsBuilding)
                {
                    AddPolygon(w, Brushes.LightPink, canvas);
                }
            }
        }

        private void DoOsmPlotLeisureGarden(Canvas canvas)
        {
            if (_surface.XScale < 10000)
                return;
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsLeisureGarden)
                {
                    AddPolygon(w, Brushes.LightYellow, canvas);
                }
            }
        }



        void DoOsmPlotWaterway(Canvas canvas)
        {
            int size = 1;

            if (_surface.XScale >= 1000)
                size = 2;

            if (_surface.XScale >= 1000)
                size = 4;

            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsWaterway)
                {
                    AddPolyline(w, Brushes.Blue, size, canvas, PolylineOnEnter, null);
                }
            }
        }

        void DoOsmPlotAeroway(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsAeroway)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = 6;
                        if (_surface.XScale <= 1000)
                            size /= 2;

                        if (_surface.XScale <= 100)
                            size /= 2;

                        if (size < 1)
                            size = 1;

                        AddPolyline(w, Brushes.DarkGray, size, canvas, PolylineOnEnter, null);

                    }
                }
            }
        }

        void DoOsmPlotFerry(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsFerry)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = 2;
                        if (_surface.XScale <= 1000)
                            size /= 2;

                        if (size < 1)
                            size = 1;

                        AddPolyline(w, Brushes.Brown, size, canvas, PolylineOnEnter, null);
                    }
                }
            }
        }

        void DoOsmPlotRailway(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsRailway)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = 6;
                        if (_surface.XScale <= 1000)
                            size /= 2;

                        if (_surface.XScale <= 100)
                            size /= 2;

                        if (size < 1)
                            size = 1;

                        AddPolyline(w, Brushes.Gray, size, canvas, PolylineOnEnter, null);
                    }
                }
            }
        }

        void DoOsmRoadPicker(Canvas canvas)
        {
            DoOsmRoadPickerNotSelected(canvas);
            DoOsmRoadPickerSelected(canvas, RouteManager.GetSelection);
        }

        void DoOsmRoadPickerSelected(Canvas canvas, Dictionary<int, Trek> selection)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsRoad)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        if (selection.ContainsKey(w._id))
                        {
                            double size = w.RoadWidth;
                            if (_surface.XScale <= 1000)
                                size /= 2;

                            if (_surface.XScale <= 100)
                                size /= 2;

                            if (size < 1)
                                size = 1;

                            AddPolyline(w, Brushes.Red, size, canvas, null, ToggleOnMouseButtonDown);
                            PlaceBlob(w, canvas);
                        }
                    }
                }
            }
        }


        void DoOsmRoadPickerNotSelected(Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsRoad)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = w.RoadWidth;
                        if (_surface.XScale <= 1000)
                            size /= 2;

                        if (_surface.XScale <= 100)
                            size /= 2;

                        if (size < 1)
                            size = 1;

                        AddPolyline(w, Brushes.Gray, size, canvas, null, ToggleOnMouseButtonDown);

                    }
                }
            }
        }

        void DoOsmPlotRoad(bool back, Canvas canvas)
        {
            foreach (OsmWay w in Osm.Ways.Values)
            {
                if (_surface.WayVisible(w) && w.IsRoad)
                {
                    if (w.IsHighWayVisible(_surface.XScale))
                    {
                        double size = w.RoadWidth;

                        if (_surface.XScale <= 10000)
                            size /= 2;

                        if (_surface.XScale <= 1000)
                            size /= 2;

                        if (_surface.XScale <= 100)
                            size /= 2;

                        if (size < 1)
                            size = 1;

                        if (back)
                        {
                            if (size >= 1)
                            {
                                AddPolyline(w, Brushes.Black, size + 2, canvas, PolylineOnEnter, null);
                            }
                        }
                        else
                        {
                            AddPolyline(w, Brushes.Red, size, canvas, PolylineOnEnter, null);
                        }
                    }
                }
            }
        }

        private void PlaceCommunityBlobNode(OsmNode n, Canvas canvas)
        {
            if (!n.IsPlace)
                return;

            double px = _surface.GetPx(n._wgs.Longitude);
            double py = _surface.GetPy(n._wgs.Latitude);
            if (py > ((Grid)canvas.Parent).Height)
                return;

            if (px > ((Grid)canvas.Parent).Width)
                return;

            if (py < 0)
                return;

            if (px < 0)
                return;

            SolidColorBrush br = new SolidColorBrush(Colors.DarkGray);
            br.Opacity = 0.5;

            double size = 8;

            if (n.IsCity)
                size = 20;


            if (_surface.XScale >= 1000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 10000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 50000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 100000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 500000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 1000000)
            {
                size *= 2;
            }

            var el = new Ellipse { Height = size, Width = size, Tag = n, StrokeThickness = 0, Fill = br };
            el.StrokeThickness = 1;
            el.Stroke = Brushes.Black;

            el.MouseEnter += MouseEnterBlob;

            Canvas.SetLeft(el, px - size / 2);
            Canvas.SetTop(el, py - size / 2);
            canvas.Children.Add(el);
        }

        private void PlaceBuildingBlobNode(OsmNode n, Canvas canvas)
        {
            if (n.IsPlace)
                return;

            double px = _surface.GetPx(n._wgs.Longitude);
            double py = _surface.GetPy(n._wgs.Latitude);
            if (py > ((Grid)canvas.Parent).Height)
                return;

            if (px > ((Grid)canvas.Parent).Width)
                return;

            if (py < 0)
                return;

            if (px < 0)
                return;

            SolidColorBrush br = Brushes.Green;

            if (n.IsRestaurant)
            {
                br = Brushes.LightGreen;
            }

            if (n.IsTemple)
            {
                br = Brushes.LightYellow;
            }

            if (n.IsHotel)
            {
                br = Brushes.RosyBrown;
            }

            if (n.IsCafe)
            {
                br = Brushes.LightBlue;
            }

            if (n.IsPlaceToVisitLeisure)
            {
                br = Brushes.Pink;
            }

            if (n.IsPlaceToVisitBusiness)
            {
                br = Brushes.Red;
            }


            double size = 4;


            if (_surface.XScale >= 100000)
            {
                size *= 2;
            }

            if (_surface.XScale >= 200000)
            {
                size *= 2;
            }


            var el = new Ellipse { Height = size, Width = size, Tag = n, StrokeThickness = 0, Fill = br };
            el.StrokeThickness = 1;
            el.Stroke = Brushes.Black;

            el.MouseEnter += MouseEnterBlob;

            Canvas.SetLeft(el, px - size / 2);
            Canvas.SetTop(el, py - size / 2);
            canvas.Children.Add(el);
        }


        private void PlaceBlob(OsmWay w, Canvas canvas)
        {
            double size = 4;
            SolidColorBrush br = Brushes.Black;
            foreach (OsmNode n1 in w.Nodes)
            {
                double px = _surface.GetPx(n1._wgs.Longitude);
                double py = _surface.GetPy(n1._wgs.Latitude);
                if (py > ((Grid)canvas.Parent).Height)
                    continue;

                if (px > ((Grid)canvas.Parent).Width)
                    continue;

                if (py < 0)
                    continue;

                if (px < 0)
                    continue;

                var el = new Ellipse { Height = size, Width = size, Tag = w, StrokeThickness = 0, Fill = br };

                //var mySolidColorBrush = new SolidColorBrush(Color.FromArgb(255, 255, 255, 0));

                //  el.MouseDown += new MouseButtonEventHandler(ElMouseDown);
                el.MouseEnter += MouseEnterBlob;
                Canvas.SetLeft(el, px - 2);
                Canvas.SetTop(el, py - 2);
                canvas.Children.Add(el);
            }
        }

        void MouseEnterBlob(object o, MouseEventArgs args)
        {
            if (_delegateReport == null)
                return;

            Ellipse p = o as Ellipse;
            if (p == null)
                return;
            OsmWay w = p.Tag as OsmWay;
            if (w == null)
            {

                OsmNode node = p.Tag as OsmNode;
                if (node != null)
                {
                    if (node._tags.Count != 0)
                    {
                        foreach (var kv in node._tags)
                        {
                            if (kv.Key.Length != 0)
                            {
                                _delegateReport("" + kv.Key + " " + kv.Value);
                            }
                        }
                        _delegateReport("++++++++++++++++++");
                    }
                }

                return;
            }


            foreach (var t in w._tags)
            {
                foreach (string v in t.Value)
                {
                    if (t.Key.Length != 0)
                    {
                        _delegateReport("" + t.Key + " " + v);
                    }
                }
            }

            _delegateReport("++++++++++++++++++");
        }

        void PolylineOnEnter(object o, MouseEventArgs args)
        {
            if (_delegateReport == null)
                return;

            Polyline p = o as Polyline;
            if (p == null)
                return;
            OsmWay w = p.Tag as OsmWay;
            if (w == null)
                return;

            foreach (var t in w._tags)
            {
                foreach (string v in t.Value)
                {
                    if (t.Key.Length != 0)
                    {
                        _delegateReport("" + t.Key + " " + v);
                    }
                }
            }

            _delegateReport("++++++++++++++++++");
        }

        private void AddPolyline(OsmWay way, SolidColorBrush br, double size, Canvas canvas, MouseEventHandler mouseEnterHandler, MouseButtonEventHandler mouseDownHandler)
        {
            var polly = new Polyline
                            {
                                StrokeThickness = size,
                                Stroke = br,
                                Points = new PointCollection(),
                                StrokeStartLineCap = PenLineCap.Round,
                                StrokeEndLineCap = PenLineCap.Round,
                                StrokeLineJoin = PenLineJoin.Round
                            };

            if (mouseEnterHandler != null)
            {
                polly.MouseEnter += mouseEnterHandler;
            }

            if (mouseDownHandler != null)
            {
                polly.MouseDown += mouseDownHandler;
            }
            polly.Tag = way;

            foreach (OsmNode n1 in way.Nodes)
            {
                double px = _surface.GetPx(n1._wgs.Longitude);
                double py = _surface.GetPy(n1._wgs.Latitude);
                polly.Points.Add(new Point(px, py));
            }

            if (polly.Points.Count != 0)
                canvas.Children.Add(polly);
        }


        private void AddPolygon(OsmWay n, SolidColorBrush br, Canvas canvas)
        {
            var polly = new Polyline
            {
                StrokeThickness = 0,
                Fill = br,
                Points = new PointCollection(),
                StrokeStartLineCap = PenLineCap.Round,
                StrokeEndLineCap = PenLineCap.Round,
                StrokeLineJoin = PenLineJoin.Round
            };

            // polly.MouseDown += new MouseButtonEventHandler(ElMouseDown);

            polly.MouseEnter += new MouseEventHandler(PolylineOnEnter);

            polly.Tag = n;

            foreach (OsmNode n1 in n.Nodes)
            {
                double px = _surface.GetPx(n1._wgs.Longitude);
                double py = _surface.GetPy(n1._wgs.Latitude);
                polly.Points.Add(new Point(px, py));
            }

            if (polly.Points.Count != 0)
                canvas.Children.Add(polly);
        }
    }

    internal class MyComparer : IComparer<int>
    {
        OsmNode _firstNode;

        public MyComparer(OsmNode firstNode)
        {
            _firstNode = firstNode;
        }


        double GetDistance(int wayId)
        {
            OsmNode firstX = (OsmNode)((OsmWay)Osm.Ways[wayId]).Nodes[0];

            int countX = ((OsmWay)Osm.Ways[wayId]).Nodes.Count;
            OsmNode lastX = (OsmNode)((OsmWay)Osm.Ways[wayId]).Nodes[countX - 1];


            double firstDistance = firstX._wgs.Distance(_firstNode._wgs);
            double lastDistance = lastX._wgs.Distance(_firstNode._wgs);

            return Math.Min(firstDistance, lastDistance);

        }

        public int Compare(int x, int y)
        {

            double dx = GetDistance(x);
            double dy = GetDistance(y);

            return (int)((dx - dy) * 1000);

        }
    }
}
